package org.apache.commons.graph.algorithms.shortestpath

import org.apache.commons.graph.VertexPair
import org.apache.commons.graph.weight.WeightedPath
import org.apache.commons.graph.algorithms.shortestpath.exception.PathNotFoundException
import org.apache.commons.graph.weight.OrderedMonoid

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/

/**
 * Represents all shortest paths between all vertex pairs calculated by [FloydWarshall] algorithm.
 *
 * @param <V> the Graph vertices type
 * @param <WE> the Graph weighted edges type
 * @param <W> the weight type
</W></WE></V> */
class AllVertexPairsShortestPath<V, WE, W>
internal constructor(private val weightOperations: OrderedMonoid<W>) {

    private val paths: MutableMap<VertexPair<V>, WeightedPath<V, WE, W>> = HashMap()

    private val shortestDistances: MutableMap<VertexPair<V>, W> = HashMap()

    /**
     * @param source
     * @param target
     * @param distance
     */
    fun addShortestDistance(source: V, target: V, distance: W) {
//        var source = source
//        var target = target
//        var distance = distance
//        source = Assertions.checkNotNull(source, "Impossible to add a shortest path from a null source")
//        target = Assertions.checkNotNull(target, "Impossible to add a shortest path to a null target")
//        distance = Assertions.checkNotNull(distance, "Impossible to add a shortest distance with a null distance")

        shortestDistances[VertexPair(source, target)] = distance
    }

    /**
     * @param source
     * @param target
     * @param weightedPath
     */
    fun addShortestPath(source: V, target: V, weightedPath: WeightedPath<V, WE, W>) {
//        var source = source
//        var target = target
//        var weightedPath = weightedPath
//        source = Assertions.checkNotNull(source, "Impossible to add a shortest path from a null source")
//        target = Assertions.checkNotNull(target, "Impossible to add a shortest path to a null target")
//        weightedPath =
//            Assertions.checkNotNull(weightedPath, "Impossible to add a null weightedPath path to a null target")

        paths[VertexPair(source, target)] = weightedPath
    }

    /**
     * Returns the shortest path between source and target
     *
     * @param source The source Vertex
     * @param target The target Vertex
     * @return Returns the shortest path between source and target
     */
    fun findShortestPath(source: V, target: V): WeightedPath<V, WE, W> {
//        var source = source
//        var target = target
//        source = Assertions.checkNotNull(source, "Impossible to add a shortest path from a null source")
//        target = Assertions.checkNotNull(target, "Impossible to add a shortest path to a null target")

        val path = paths[VertexPair(source, target)] ?: throw PathNotFoundException("Path from '%s' to '%s' doesn't exist", source, target)

        return path
    }

    /**
     * Returns the shortest distance between source and target.
     *
     * @param source The source Vertex
     * @param target The target Vertex
     * @return Returns the shortest distance between source and target.
     */
    fun getShortestDistance(source: V, target: V): W? {
//        var source = source
//        var target = target
//        source = Assertions.checkNotNull(source, "Impossible to add a shortest path from a null source")
//        target = Assertions.checkNotNull(target, "Impossible to add a shortest path to a null target")

        if (source == target) {
            return weightOperations.identity()
        }

        return shortestDistances[VertexPair(source, target)]
    }

    /**
     * Checks if there is a shortest distance between source and target.
     *
     * @param source The source Vertex
     * @param target The target Vertex
     * @return Returns true if there is a shortest distance between source and target, false otherwise.
     */
    fun hasShortestDistance(source: V, target: V): Boolean {
//        var source = source
//        var target = target
//        source = Assertions.checkNotNull(source, "Impossible to add a shortest path from a null source")
//        target = Assertions.checkNotNull(target, "Impossible to add a shortest path to a null target")

        if (source == target) {
            return true
        }

        return shortestDistances.containsKey(VertexPair(source, target))
    }

    override fun toString(): String {
        return shortestDistances.toString()
    }
}
